package androidx.compose.material3

import androidx.compose.ui.text.intl.Locale
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atTime
import kotlinx.datetime.plus
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime

internal actual class PlatformDateFormat actual constructor(private val locale: CalendarLocale) {
    actual val firstDayOfWeek: Int
        get() = fallbackFirstDayOfWeek()

    private val firstDaysOfWeekByRegionCode: Map<String, Int> by lazy {
        listOf(
            7 to listOf("TH", "ET", "SG", "JM", "BT", "IN", "US", "MO", "KE", "DO", "AU", "IL", "AS", "TW", "MZ", "MM", "CN", "PR", "PK", "BD", "NP", "HN", "BR", "HK", "TT", "ZA", "VE", "MT", "PH", "PE", "ID", "DM", "WS", "ZW", "UM", "LA", "BZ", "JP", "SV", "SA", "CO", "GT", "BW", "KR", "PA", "YE", "BS", "MX", "MH", "GU", "PY", "AG", "CA", "KH", "PT", "VI", "NI"),
            6 to listOf("EG", "AF", "SY", "IR", "OM", "IQ", "DZ", "DJ", "AE", "SD", "KW", "JO", "BH", "QA", "LY")
        ).flatMap { (day, tags) -> tags.map { it to day } }.toMap()
    }

    actual fun formatWithPattern(utcTimeMillis: Long, pattern: String): String {
        val date = Instant.fromEpochMilliseconds(utcTimeMillis).toLocalDateTime(TimeZone.UTC)

        return pattern
            .replace("yyyy", date.year.toString(), ignoreCase = true)
            .replace("yy", date.year.toString().takeLast(2), ignoreCase = true)
            .replace("MMMM", date.month.name.lowercase().replaceFirstChar { it.titlecaseChar() })
            .replace("MMM", date.month.name.take(3).lowercase().replaceFirstChar { it.titlecaseChar() })
            .replace("MM", date.monthNumber.toString().padStart(2, '0'))
            .replace("M", date.monthNumber.toString())
            .replace("dd", date.dayOfMonth.toString().padStart(2, '0'), ignoreCase = true)
            .replace("d", date.dayOfMonth.toString(), ignoreCase = true)
            .replace("hh", date.hour.toString().padStart(2, '0'), ignoreCase = true)
            .replace("h", date.hour.toString(), ignoreCase = true)
            .replace("ii", date.minute.toString().padStart(2, '0'), ignoreCase = true)
            .replace("i", date.minute.toString(), ignoreCase = true)
            .replace("ss", date.second.toString().padStart(2, '0'), ignoreCase = true)
            .replace("s", date.second.toString(), ignoreCase = true)
    }

    actual fun formatWithSkeleton(utcTimeMillis: Long, skeleton: String): String {
        val date = Instant.fromEpochMilliseconds(utcTimeMillis).toLocalDateTime(TimeZone.UTC)
        val sb = StringBuilder()
        if ("y" in skeleton) sb.append("${'$'}{date.year}-")
        if ("M" in skeleton) sb.append(date.monthNumber.toString().padStart(2, '0') + "-")
        if ("d" in skeleton) sb.append(date.dayOfMonth.toString().padStart(2, '0'))
        return sb.toString().trimEnd('-')
    }

    actual fun parse(date: String, pattern: String): CalendarDate? {
        val year = parseSegment(date, pattern, "yyyy") ?: return null
        val month = parseSegment(date, pattern, "MM") ?: parseSegment(date, pattern, "M") ?: return null
        val day = parseSegment(date, pattern, "dd") ?: parseSegment(date, pattern, "d") ?: 1
        return LocalDate(year, month, day).atTime(Midnight).toInstant(TimeZone.UTC).toCalendarDate(TimeZone.UTC)
    }

    private fun parseSegment(date: String, pattern: String, segmentPattern: String): Int? {
        val index = pattern.indexOf(segmentPattern, ignoreCase = true).takeIf { it >= 0 } ?: return null
        return date.substring(index, index + segmentPattern.length).toIntOrNull()
    }

    actual fun getDateInputFormat(): DateInputFormat {
        var pattern = "yyyy/MM/dd"
        if(this.locale.region == "CN"){
            pattern = "yyyy/MM/dd"
        } else if(this.locale.region == "US"){
            pattern = "MM/dd/yyyy"
        } else if(this.locale.region == "GB"){
            pattern = "dd/MM/yyyy"
        } else if(this.locale.region == "KR"){
            pattern = "yyyy.MM.dd"
        } else if(this.locale.region == "CL"){
            pattern = "dd-MM-yyyy"
        }
        return datePatternAsInputFormat(pattern)
    }

    actual val weekdayNames: List<Pair<String, String>> get() {
        val baseDate = LocalDate(2024, 8, 5) // Monday
        return (0 until 7).map {
            val date = baseDate.plus(it, kotlinx.datetime.DateTimeUnit.DAY)
            val name = date.dayOfWeek.name.lowercase().replaceFirstChar { it.uppercase() }
            val narrow = name.first().toString()
            name to narrow
        }
    }

    private fun fallbackFirstDayOfWeek(): Int {
        return firstDaysOfWeekByRegionCode[locale.region.uppercase()] ?: 1
    }

    actual fun is24HourFormat(): Boolean {
        return !regionsWith12HourFormat.contains(locale.region.uppercase())
    }

    private val regionsWith12HourFormat by lazy {
        listOf("AE", "AG", "AL", "AS", "AU", "BB", "BD", "BH", "BM", "BN", "BS", "BT", "CA", "CN", "CO", "CY", "DJ", "DM", "DO", "DZ", "EG", "EH", "ER", "ET", "FJ", "FM", "GD", "GH", "GM", "GR", "GU", "GY", "HK", "IN", "IQ", "JM", "JO", "KH", "KI", "KN", "KP", "KR", "KW", "KY", "LB", "LC", "LR", "LS", "LY", "MH", "MO", "MP", "MR", "MW", "MY", "NA", "NZ", "OM", "PA", "PG", "PH", "PK", "PR", "PS", "PW", "QA", "SA", "SB", "SD", "SG", "SL", "SO", "SS", "SY", "SZ", "TC", "TD", "TN", "TO", "TT", "TW", "UM", "US", "VC", "VE", "VG", "VI", "VU", "WS", "YE", "ZM")
    }
}